package com.changdu.seatunnel.admin.config;

import java.io.IOException;
import java.net.ConnectException;
import java.util.List;
import java.util.Objects;

import org.springframework.context.MessageSourceResolvable;
import org.springframework.http.HttpStatus;
import org.springframework.http.converter.HttpMessageNotWritableException;
import org.springframework.validation.BindException;
import org.springframework.validation.FieldError;
import org.springframework.validation.ObjectError;
import org.springframework.web.HttpMediaTypeNotAcceptableException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.method.annotation.HandlerMethodValidationException;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
import org.springframework.web.servlet.NoHandlerFoundException;
import org.springframework.web.servlet.resource.NoResourceFoundException;

import com.changdu.seatunnel.admin.config.common.BusinessException;
import com.changdu.seatunnel.admin.config.model.ExceptionResult;
import com.changdu.seatunnel.admin.util.LogUtils;
import com.changdu.seatunnel.admin.util.RequestAnalysor;
import com.changdu.seatunnel.admin.util.Throwabler;

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;


/**
 * 全局异常处理
 * @author wangxiaoliang
 */
@RestControllerAdvice
public class GlobalExceptionAdvice {

	@ResponseBody
	@ExceptionHandler(BusinessException.class)
	public ExceptionResult businessException(HttpServletRequest request, BusinessException e) {
		String uri = RequestAnalysor.getUri(request);
		String stackTrace = Throwabler.getFirstStackInfo(e);
		LogUtils.error(e.getMessage(), e);
		return new ExceptionResult(e.getMessage(), e.getCode(), stackTrace, uri);
	}

	@ResponseBody
	@ExceptionHandler(value = BindException.class)
	public ExceptionResult validationException(HttpServletRequest request, BindException e) {
		String uri = RequestAnalysor.getUri(request);
		String message = e.getAllErrors().get(0).getDefaultMessage();
		// 在这里判断是类型转换异常还是校验异常
		if (Objects.nonNull(message) && message.contains("Exception")) {
			FieldError objectError = (FieldError) e.getAllErrors().get(0);
			String field = objectError.getField();
			String fieldNew = field.replaceAll("[A-Z]", "_$0").toLowerCase();
			return new ExceptionResult(message, HttpStatus.BAD_REQUEST.value(), String.format("%s: 参数格式错误！", fieldNew), uri);
		}
		return new ExceptionResult(message, HttpStatus.BAD_REQUEST.value(), e.getMessage(), uri);
	}

	@ResponseBody
	@ExceptionHandler(value = MethodArgumentTypeMismatchException.class)
	public ExceptionResult validationNumException(HttpServletRequest request, MethodArgumentTypeMismatchException e) {
		String uri = RequestAnalysor.getUri(request);
		String fieldNew = e.getName().replaceAll("[A-Z]", "_$0").toLowerCase();
		return new ExceptionResult(HttpStatus.BAD_REQUEST.getReasonPhrase(), HttpStatus.BAD_REQUEST.value(), 
				String.format("%s: parameter format error", fieldNew), uri);
	}

	@ResponseBody
	@ExceptionHandler(value = IllegalArgumentException.class)
	public ExceptionResult allegalArgumentException(HttpServletRequest request, IllegalArgumentException e) {
		String uri = RequestAnalysor.getUri(request);
		String stackTrace = Throwabler.getFirstStackInfo(e);
		return new ExceptionResult("parameter error: " + e.getMessage(), HttpStatus.BAD_REQUEST.value(), stackTrace, uri);
	}

	@ResponseBody
	@ExceptionHandler(value = ConnectException.class)
	public ExceptionResult connectException(HttpServletRequest request, ConnectException e) {
		String uri = RequestAnalysor.getUri(request);
		String stackTrace = Throwabler.getFirstStackInfo(e);
		Integer code = HttpStatus.GATEWAY_TIMEOUT.value();

		return new ExceptionResult("Unable to connect to the remote server.", code, stackTrace, uri);
	}
	
	@ResponseBody
	@ExceptionHandler(value = HandlerMethodValidationException.class)
	public ExceptionResult handlerMethodValidationException(HttpServletRequest request, HandlerMethodValidationException e) {
		String uri = RequestAnalysor.getUri(request);
		String stackTrace = Throwabler.getFirstStackInfo(e);
		MessageSourceResolvable first = e.getAllErrors().getFirst();
		Integer code = HttpStatus.BAD_REQUEST.value();
		return new ExceptionResult(first.getDefaultMessage(), code, stackTrace, uri);
	}
	
	@ResponseBody
	@ExceptionHandler(value = MethodArgumentNotValidException.class)
	public ExceptionResult methodArgumentNotValidException(HttpServletRequest request,
			MethodArgumentNotValidException e) {
		String uri = RequestAnalysor.getUri(request);
		String stackTrace = Throwabler.getFirstStackInfo(e);
		List<ObjectError> errors = e.getBindingResult().getAllErrors();
		StringBuffer errorMsg = new StringBuffer();
		errors.stream().forEach(x -> errorMsg.append(x.getDefaultMessage()).append(";"));
		String message = errorMsg.toString();
		Integer code = HttpStatus.BAD_REQUEST.value();

		return new ExceptionResult(message, code, stackTrace, uri);
	}

	@ResponseBody
	@ExceptionHandler(value = NoHandlerFoundException.class)
	public ExceptionResult noHandlerFoundException(HttpServletRequest request, NoHandlerFoundException e) {
		String uri = RequestAnalysor.getUri(request);
		String stackTrace = Throwabler.getFirstStackInfo(e);
		Integer code = HttpStatus.NOT_FOUND.value();

		return new ExceptionResult("404 not found", code, stackTrace, uri);
	}
	
	@ResponseBody
	@ExceptionHandler(value = {NoResourceFoundException.class, HttpMediaTypeNotAcceptableException.class, HttpMessageNotWritableException.class})
	public void NoResourceFoundException(HttpServletRequest request, HttpServletResponse response) {
		try {
			response.getWriter().write("404");
		} catch (IOException e) {
			e.printStackTrace();
		}
	}	
	
	@ResponseBody
	@ExceptionHandler(Exception.class)
	public ExceptionResult defaultException(HttpServletRequest request, Exception e) {
		String uri = RequestAnalysor.getUri(request);
		String stackTrace = Throwabler.getFirstStackInfo(e);
		Integer code = HttpStatus.INTERNAL_SERVER_ERROR.value();
		LogUtils.error(e.getMessage(), e);
		return new ExceptionResult(e.getMessage(), code, stackTrace, uri);
	}

}
